﻿using System;
using System.Xml;
using System.Collections.Generic;
using Microsoft.Xna.Framework;

namespace ThreeDContentPipeline
{
    public enum DeleDLayerType
    {
        NotUsed,
        Texture,
        Lightmap
    }

    public class DeleDTexture
    {
        public readonly string File;

        public DeleDTexture(XmlElement element)
        {
            File = element.GetAttribute("file");
        }
    }

    public class DeleDLayer
    {
        public readonly DeleDLayerType Type;
        public readonly DeleDTexture Texture;

        public DeleDLayer(XmlElement element)
        {
            string type = element.GetAttribute("type");

            Texture = null;
            if (type == "texture")
            {
                Type = DeleDLayerType.Texture;
            }
            else if (type == "lightmap")
            {
                Type = DeleDLayerType.Lightmap;
            }
            else
            {
                Type = DeleDLayerType.NotUsed;
            }
            if (Type != DeleDLayerType.NotUsed)
            {
                Texture = new DeleDTexture(element.ChildNodes[0] as XmlElement);
            }
        }
    }

    public class DeleDMaterial
    {
        public readonly int ID;
        public readonly Dictionary<DeleDLayerType, DeleDLayer> Layers;

        public DeleDMaterial(XmlElement element)
        {
            ID = int.Parse(element.GetAttribute("id"));
            Layers = new Dictionary<DeleDLayerType, DeleDLayer>();

            foreach (XmlElement childElement in element.ChildNodes)
            {
                if (childElement.Name == "layer")
                {
                    DeleDLayer layer = new DeleDLayer(childElement);

                    if (!Layers.ContainsKey(layer.Type))
                    {
                        Layers.Add(layer.Type, layer);
                    }
                }
            }
        }
    }

    public class DeleDMaterials : Dictionary<int, DeleDMaterial>
    {
        public DeleDMaterials(XmlElement element)
        {
            foreach (XmlElement categoryElement in element.ChildNodes)
            {
                foreach (XmlElement materialElement in categoryElement.ChildNodes)
                {
                    DeleDMaterial material = new DeleDMaterial(materialElement);

                    Add(material.ID, material);
                }
            }
        }
    }

    public class DeleDVertex
    {
        public readonly int ID;
        public readonly Vector3 Position;

        public DeleDVertex(XmlElement element)
        {
            ID = int.Parse(element.GetAttribute("id"));
            Position.X = float.Parse(element.GetAttribute("x"));
            Position.Y = float.Parse(element.GetAttribute("y"));
            Position.Z = float.Parse(element.GetAttribute("z"));
        }
    }

    public class DeleDVertices : Dictionary<int, DeleDVertex>
    {
        public DeleDVertices(XmlElement element)
        {
            foreach (XmlElement vertexElement in element.ChildNodes)
            {
                DeleDVertex vertex = new DeleDVertex(vertexElement);

                Add(vertex.ID, vertex);
            }
        }
    }

    public class DeleDPolyonVertex
    {
        public readonly int VertexID;
        public readonly Vector2 TextureCoordinate1;
        public readonly Vector2 TextureCoordinate2;

        public DeleDPolyonVertex(XmlElement element)
        {
            VertexID = int.Parse(element.GetAttribute("vid"));
            TextureCoordinate1.X = float.Parse(element.GetAttribute("u0"));
            TextureCoordinate1.Y = float.Parse(element.GetAttribute("v0"));
            if (element.HasAttribute("u1"))
            {
                TextureCoordinate2.X = float.Parse(element.GetAttribute("u1"));
                TextureCoordinate2.Y = float.Parse(element.GetAttribute("v1"));
            }
            else
            {
                TextureCoordinate2 = Vector2.Zero;
            }
        }
    }

    public class DeleDPolygon : List<DeleDPolyonVertex>, IComparable<DeleDPolygon>
    {
        public readonly int MaterialID;
        public readonly int[] Indices;
        public readonly Vector3 Normal;

        public DeleDPolygon(XmlElement element, DeleDPrimitive primitive)
        {
            Vector3 a, b, c;

            MaterialID = int.Parse(element.GetAttribute("mid"));

            foreach (XmlElement vertexElement in element.ChildNodes)
            {
                Add(new DeleDPolyonVertex(vertexElement));
            }

            a = primitive.Vertices[this[0].VertexID].Position;
            b = primitive.Vertices[this[1].VertexID].Position;
            c = primitive.Vertices[this[2].VertexID].Position;

            Normal = Vector3.Normalize(Vector3.Cross(c - b, b - a));

            Indices = new int[(Count - 2) * 3];

            for (int i = 0, j = 0; i != Count - 2; i++)
            {
                Indices[j++] = 0;
                Indices[j++] = i + 1;
                Indices[j++] = i + 2;
            }
        }

        public int CompareTo(DeleDPolygon p)
        {
            return MaterialID.CompareTo(p.MaterialID);
        }
    }

    public class DeleDPolygons : List<DeleDPolygon>
    {
        public DeleDPolygons(XmlElement element, DeleDPrimitive primitive)
        {
            foreach (XmlElement polygonElement in element.ChildNodes)
            {
                Add(new DeleDPolygon(polygonElement, primitive));
            }
        }
    }

    public class DeleDPrimitive
    {
        public readonly string Name;
        public readonly DeleDVertices Vertices;
        public readonly DeleDPolygons Polygons;

        public DeleDPrimitive(XmlElement element)
        {
            Name = element.GetAttribute("name");

            foreach (XmlElement childElement in element.ChildNodes)
            {
                if (childElement.Name == "vertices")
                {
                    Vertices = new DeleDVertices(childElement);
                }
                else if (childElement.Name == "polygons")
                {
                    Polygons = new DeleDPolygons(childElement, this);
                }
            }
        }
    }

    public class DeleDPrimitives : List<DeleDPrimitive>
    {
        public DeleDPrimitives(XmlElement element)
        {
            foreach (XmlElement childElement in element.ChildNodes)
            {
                if (childElement.Name == "primitive")
                {
                    Add(new DeleDPrimitive(childElement));
                }
            }
        }
    }

    public class DeleDFile
    {
        public readonly DeleDMaterials Materials;
        public readonly DeleDPrimitives Primitives;

        public DeleDFile(string file)
        {
            XmlDocument document = new XmlDocument();

            document.Load(file);

            foreach (XmlElement element in document.DocumentElement.ChildNodes)
            {
                if (element.Name == "materials")
                {
                    Materials = new DeleDMaterials(element);
                }
                else if (element.Name == "primitives")
                {
                    Primitives = new DeleDPrimitives(element);
                }
            }
        }
    }
}
